package com.yipin.neimeng.util;

import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.alibaba.fastjson.JSONObject;
import com.asiainfo.openplatform.utils.AIESBConfig;
import com.asiainfo.openplatform.utils.RSAUtils;
import com.asiainfo.openplatform.utils.SecurityUtils;
import com.asiainfo.openplatform.utils.SignUtil;

public class HttpJsonInvokeDemo {
	private static transient Log log = LogFactory.getLog(HttpJsonInvokeDemo.class); // 记录日志

	public static String DEFAULT_CHARSET = "UTF-8"; // 默认编码
	public static String ENCRYPT_NO = "0"; // 应用不加密
	public static String ENCRYPT_ALL = "1"; // 应用整体加密


	//此处代码都放到类的静态区，初始化全局的HttpClient连接池化管理器
 	private static final int TIMEOUT = 3 * 60 * 1000;//连接和返回超时时间
 	private static final int MAX_HTTP_CONNECTION = 1000;//最大连接数
	private static final int MAX_CONNECTION_PER_HOST = 1000;//最大主机连接数，由于后面是一个负载均衡地址，所以没必要细分每个节点的连接数
	private static HttpClient httpClient;
	static{
		httpClient= new HttpClient( new MultiThreadedHttpConnectionManager());
		httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(TIMEOUT);	//连接超时
		httpClient.getHttpConnectionManager().getParams().setSoTimeout(TIMEOUT);//请求超时
		httpClient.getHttpConnectionManager().getParams().setMaxTotalConnections(MAX_HTTP_CONNECTION);//连接池最大连接数
		httpClient.getHttpConnectionManager().getParams().setDefaultMaxConnectionsPerHost( MAX_CONNECTION_PER_HOST);//最大单主机连接数
	}


	/**
	 * 发起ESB调用
	 *
	 * @param esbUrl
	 *            调用地址
	 * @param sysParam
	 *            系统参数
	 * @param busiParam
	 *            业务参数
	 * @return
	 * @throws Exception
	 */
	public static String call(String esbUrl, String sysParam, String busiParam)
			throws Exception {

		// 组装请求地址
		StringBuffer httpUrl = new StringBuffer();
		httpUrl.append(esbUrl);

		if (!StringUtils.isEmpty(sysParam)) {
			httpUrl.append("?").append(sysParam);
		}

		log.info("发起调用："+httpUrl);

		PostMethod postMethod = new PostMethod(httpUrl.toString());

		String response = "";
		try {
			// 报文发送
			postMethod.setRequestHeader("Content-type",
					"application/json; charset=UTF-8");
			RequestEntity entity = new StringRequestEntity(busiParam,
					"application/json", DEFAULT_CHARSET);
			postMethod.setRequestEntity(entity);
			httpClient.executeMethod(postMethod);
			response = new String(postMethod.getResponseBodyAsString()
					.getBytes("ISO-8859-1"), DEFAULT_CHARSET);

		} finally {
			if (postMethod != null) {
				postMethod.releaseConnection();
			}
		}

		return response;
	}

	/**
	 * 报文加密 - 返回加密报文
	 *
	 *
	 * @param busiParam
	 *            待加密的业务报文 , encryptEnv:加密环境. 0:不加密, 1:整体加密 encryptKey:加密的密匙
	 *            encryptType:加密类型
	 * @throws Exception
	 * @author xiaohang3
	 *
	 */
	public static String encryptMessage(String busiParam, String encryptEnv,
										String encryptKey, String encryptType) throws Exception {
		String encryptionContent = "";

		if (StringUtils.isEmpty(busiParam)) {
			return "";
		}

		// 加密环境指派为不加密,直接返回
		if (ENCRYPT_NO.equals(encryptEnv)) {
			return busiParam;
		}

		try {
			if (encryptType.endsWith("RSA")) {
				encryptionContent = RSAUtils.encryptByPublicKey(busiParam,
						encryptKey);
			} else if (encryptType.endsWith("AES")) {
				encryptionContent = SecurityUtils.encodeAES256HexUpper(
						busiParam, SecurityUtils.decodeHexUpper(encryptKey));
			} else {
				encryptionContent = busiParam;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return encryptionContent;
	}

	/**
	 * 报文解密 - 返回result结点里面的解密报文
	 *
	 * @param message
	 *            待解密报文; encryptEnv: 解密环境. 0:不解密. 1:整体解密
	 *            encryptKey:解密密匙,和加密密匙一致; encryptType:解密类型,和加密类型一致
	 * @return 解密后的报文
	 * @throws Exception
	 *
	 */
	public static String decryptMessage(String message, String encryptEnv,
			String encryptKey, String encryptType) throws Exception {

		// 返回默认
		message = StringUtils.isEmpty(message) ? "{}" : message;
		// 目前CRM返回参数被能力开放平台包了一层result, 加密内容放在result里面.
		// 后续会叫能力开放平台开发人员修改出参结构,改为正常业务参数返回.
		String encryptMessage = message;
		String decryptMessage = message; // 默认情况下,解密参数与返回参数一致

		// 加密环境指派为不加密,直接返回
		if (ENCRYPT_NO.equals(encryptEnv)) {
			return decryptMessage;
		}

		try {
			if (!StringUtils.isEmpty(encryptMessage)) {
				if (encryptType.endsWith("AES")) {
					decryptMessage = SecurityUtils.decodeAES256HexUpper(
							encryptMessage,
							SecurityUtils.decodeHexUpper(encryptKey));
				} else if (encryptType.endsWith("RSA")) {
					// decryptMessage =
					// SecurityUtils.decryptByPublicKey(encryptMessage,
					// encryptKey);
					decryptMessage = RSAUtils.decryptByPrivateKey(
							encryptMessage, encryptKey);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return decryptMessage;
	}

	/**
	 * 能力开放平台---报文签名. 应用整体加密
	 *
	 * @param sysParam
	 *            :系统参数 ; busiParam:业务参数 ; signKey:签名密匙 ; signType:签名类型
	 * @throws Exception
	 * @author xiaohang3
	 * @date 2016-08-04
	 */
	private static String signMessage(String sysParam, String busiParam,
			String signKey, String signType) throws Exception {

		// 公共参数
		Map<String, String> sysParamMap = new HashMap<String, String>();
		String[] sysKeyValue = sysParam.split("&");
		for (int i = 0; i < sysKeyValue.length; i++) {
			String[] sysParamStr = sysKeyValue[i].split("=");
			sysParamMap.put(sysParamStr[0], sysParamStr[1]);
		}

		StringBuffer sysParamSb = new StringBuffer(); // 返回的公共参数

		String sign = "";
		try {
			if (signType.equals("RSA")) {
				// sign = SignUtil.sign(sysParamMap, busiParam, signKey);
				sign = SignUtil.generateSign(sysParamMap, busiParam);
			} else if (signType.equals("SHA")) {
				// sign = SignUtil.sign(sysParamMap, busiParam,
				// "HmacSHA256",signKey);
			}
			// 组装公共参数,网络传输的原因,需要编码
			for (Map.Entry<String, String> entry : sysParamMap.entrySet()) {
				String key = entry.getKey();
				String value = entry.getValue();
				if (!StringUtils.isEmpty(value)) {
					value = URLEncoder.encode(value, DEFAULT_CHARSET);
				}
				sysParamSb.append(key).append("=").append(value).append("&");
			}

			sysParamSb.append("sign=").append(
					URLEncoder.encode(sign, DEFAULT_CHARSET));
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (log.isInfoEnabled()) {
			log.info("本客户端字串,sign:" + sign);
		}

		return sysParamSb.toString();
	}

	public static void main(String[] args) throws Exception {

		// 应用Id
		String appId = AIESBConfig.getAppId();
		// 报文格式
		String format = "json";
		// 加密环境. 有0(不加密),1(整体加密),2(部分加密)两种. 应用固定为整体加密.
		String encryptEnv = AIESBConfig.getValue("encrypt_env");
		// 应用密钥,根据具体的应用,填写不同的应用密钥
		String secKey = AIESBConfig.getAppKey();
		// 应用公玥,根据具体的应用,填写不同的应用公钥
		String pubKey = AIESBConfig.getRsaPublicKey();
		// 加密方式,有RSA和AES两种. 默认为AES.没有则不填写
		String encryptType = AIESBConfig.getValue("encrypt_type");
		// 签名方式,有SHA和RSA两种. 默认RSA,没有则不填写
		String signType = AIESBConfig.getSignMethod();
		// 获取token的Url
		String tokenHttpUri = AIESBConfig.getValue("token_http_uri");
		// 应用回调地址,用于获取token需要的参数
		String redirectUri = AIESBConfig.getValue("redirect_uri");

		// 能力编号,要调用的哪个应用的哪个能力
		String abilityCode = "cust_IQueryChanceCSV_queryChanceInfosByCond";

		/*
		 * 统参数示例:
		 * method=CRM_SO_PERSON_OI_ORDINFOQRY_001&format=json&appId=501085
		 * &timestamp=20141204111800&token=xxx&busiSerial=xxx
		 * 其中token需要调用权限认证平台获取.busiSerial(三方流水号)需要三方系统自己生成.
		 */

		// 获取token,grant_type固定为client_credential(客户端模式),app_id为应用id,app_key为应用密钥,redirect_uri为应用回调地址;信息均在开发者视图.
		// 通过http调用返回token字符串
		String tokenUrl = tokenHttpUri
				+ "?grant_type=client_credentials&app_id=" + appId
				+ "&app_key=" + secKey + "&redirect_uri=" + redirectUri;

		String token = call(tokenUrl, "", "");
		JSONObject tokenJson = (JSONObject) JSONObject.parse(token);
		token = tokenJson.getString("access_token");
		if (log.isInfoEnabled()) {
			log.info("本客户端字串,token:" + token);
		}

		// 外围三方流水号
		String busiSerial = "2015060611223320345443";


		//公共参数,按需填写
		JSONObject reqJson = new JSONObject();
		JSONObject pubJson = new JSONObject();
		JSONObject pageJson = new JSONObject();

		pubJson.put("OP_CODE", "SYS71040800");
		pubJson.put("REQ_SERIAL_NO", "201609062155");
		pubJson.put("CHANNEL_ID", "25");
		pubJson.put("OP_ORG_ID", "10002268");

		pageJson.put("PAGE_SIZE", "2");
		pageJson.put("CURRENT_PAGE", "1");

		reqJson.put("PUB_INFO", pubJson);
		reqJson.put("PAGE_INFO", pageJson);

		// 业务参数,Json格式
		JSONObject busiJson = new JSONObject();
		busiJson.put("OFFER_ID", "9100000007");

		reqJson.put("BUSI_INFO", busiJson);

		JSONObject inputJson = new JSONObject();
		inputJson.put("REQ_PARAM", reqJson);

		String busiParam = inputJson.toString();
		try {
			// 3.0版本http接入；如果是2.0版本，最后要加上appKey这个参数, 这是直接调用的。
			/*String http_response = AIESBClient.execute(sysParam, busiParam,
					AIESBConstants.PROTOCOL.HTTP,secKey);
			System.out.println("http request response:" + http_response);*/

			String sysParamUrl = "method="
					+ abilityCode
					+ "&format="
					+ format
					+ "&appId="
					+ appId
					+ "&timestamp="
					+ new SimpleDateFormat("yyyyMMddHHmmss").format(Calendar
							.getInstance().getTime()) + "&accessToken=" + token
					+ "&busiSerial=" + busiSerial;

			if (log.isInfoEnabled()) {
				log.info("本客户端字串,sysParamUrl:" + sysParamUrl);
			}


			// 业务报文加密, 加密环境固定成应用整体加密
			String encryptKey = "";
			if (!StringUtils.isEmpty(encryptType)) {
				encryptKey = encryptType.equals("RSA") ? pubKey : secKey;
			} else {
				encryptKey = "";
			}


			log.info("输入报文（未加密）:"+busiParam);
			busiParam = encryptMessage(inputJson.toString(), encryptEnv,
					encryptKey, encryptType);

			log.info("输入报文（加密）:"+busiParam);


			// 应用整体签名,得到最终的系统参数
			String signKey = signType.equals("RSA") ? pubKey : secKey;
			String sysParam = signMessage(sysParamUrl.toString(), busiParam,
					signKey, signType);

			// 发起调用
			String esbUrl = AIESBConfig.getHttpConnectUrl();	//Http请求地址。
			String response = call(esbUrl, sysParam, busiParam);

			log.info("返回报文（未解密）: " + response);

			// 解密返回报文, 解密环境固定为应用整体解密.
			response = decryptMessage(response, encryptEnv, encryptKey, encryptType);

			log.info("返回报文（解密）: " + response);

			// https接入；如果是2.0版本，最后要加上appKey这个参数
		/*	String https_response = AIESBClient.execute(sysParam, busiParam,
					AIESBConstants.PROTOCOL.HTTPS);
			System.out.println("https request response:" + https_response);*/

			/*
			 * ps: webservice,remote和socket的接入请求和以上类似，只需将AIESBClient.
			 * execute的第三个参数protocol替换成对应的
			 * AIESBConstants.PROTOCOL.WEBSERVICE,AIESBConstants
			 * .PROTOCOL.REMOTE或AIESBConstants.PROTOCOL.SOCKET即可。
			 */
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
